/*******************************************************************************
 * Copyright (c) 2006 Ultimate Technology, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Bojan Vukojevic - initial API and implementation
 *******************************************************************************/
package com.ultimatetech.cim.views;


import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Vector;

import javax.wbem.cim.CIMDataType;
import javax.wbem.cim.CIMDateTime;
import javax.wbem.cim.CIMException;
import javax.wbem.cim.CIMInstance;
import javax.wbem.cim.CIMProperty;
import javax.wbem.cim.CIMValue;
import javax.wbem.client.CIMClient;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;

import com.ultimatetech.cim.CIMPlugin;
import com.ultimatetech.cim.preferences.CIMPreferencePage;
import com.ultimatetech.cim.views.content.CIMConnect;


/**
 * This sample class demonstrates how to plug-in a new
 * workbench view. The view shows data obtained from the
 * model. The sample creates a dummy model on the fly,
 * but a real implementation would connect to the model
 * available either in this or another plug-in (e.g. the workspace).
 * The view is connected to the model using a content provider.
 * <p>
 * The view uses a label provider to define how model
 * objects should be presented in the view. Each
 * view can present the same model objects using
 * different labels and icons, if needed. Alternatively,
 * a single label provider can be shared between views
 * in order to ensure that objects of the same type are
 * presented in the same way everywhere.
 * <p>
 */

public class CIMInstanceView extends ViewPart {
	private static String COLUMN_NAMES[] = new String[] {
		"Property Name", "Value", "Data Type"
	};
	private TableViewer viewer;
	private Action setInstanceAct;
	private Action deleteInstance;
	private Action refreshAction;
	//private Action doubleClickAction;
	private CIMInstance cimInstance = null;
	private CIMPropChangeListener propChangeListener ;
	private Label copLabel = null;
	Composite mainComposite = null;
	Composite tableComp = null;
	List<String> keysList = null;
	List<String> changedProps = null;
	
	class CIMPropChangeListener implements IPropertyChangeListener {

		public void propertyChange(org.eclipse.jface.util.PropertyChangeEvent event) {
			boolean cimPropchanged = false;
			if (event.getProperty().equals(CIMPreferencePage.PreferenceNames.CIM_HOST)) {
				cimPropchanged = true;
			} else if(event.getProperty().equals(CIMPreferencePage.PreferenceNames.CIM_NAMESPACE)){
				cimPropchanged = true;
			} else if(event.getProperty().equals(CIMPreferencePage.PreferenceNames.CIM_PORT)){
				cimPropchanged = true;
			}else if(event.getProperty().equals(CIMPreferencePage.PreferenceNames.CIM_FILTER)){
				cimPropchanged = true;
			}
			if(cimPropchanged){				
				clearView();
			}
		}		
 	}
	
	private void clearView() {
		cimInstance = null;
		viewer.getTable().removeAll();					
		viewer.setContentProvider(new ViewContentProvider());
		viewer.setLabelProvider(new ViewLabelProvider());
		viewer.setSorter(new NameSorter());
		viewer.setInput(getViewSite());		
		viewer.refresh();
	}
	
	/*
	 * The content provider class is responsible for
	 * providing objects to the view. It can wrap
	 * existing objects in adapters or simply return
	 * objects as-is. These objects may be sensitive
	 * to the current input of the view, or ignore
	 * it and always show the same content 
	 * (like Task List, for example).
	 */
	 
	class ViewContentProvider implements IStructuredContentProvider {
		public void inputChanged(Viewer v, Object oldInput, Object newInput) {
		}
		public void dispose() {
		}
		public Object[] getElements(Object parent) {
			if(cimInstance != null){
				Vector<?> v = cimInstance.getProperties();
				return v.toArray();
			}
			return new String[] { "Double click on the instance in CIM Explorer to show it here...." };
		}
	}
	class ViewLabelProvider extends LabelProvider implements ITableLabelProvider {
		public String getColumnText(Object obj, int index) {
			String result = "";
			if(obj instanceof CIMProperty) {
			CIMProperty cop = (CIMProperty) obj;
			
			switch (index) {
				case 0: 
					result = cop.getName();
					break;
				case 1 :
					result = cop.getValue() == null ? "null" : cop.getValue().toMOF();
					if(cop.getType().isArrayType()){
						if(cop.getType().getType() == CIMDataType.STRING_ARRAY){
							StringBuffer finalRes = new StringBuffer("");
							String res[] = result.split(",");
							for(int i = 0; i < res.length; i++){
								if(i > 0){
									finalRes.append(", ");
								}
								res[i] = stripStartEndQuote(res[i]);
								finalRes.append(res[i]);
							}
							result = finalRes.toString();							
						}
					} else if(cop.getType().getType() == CIMDataType.STRING){
						result = stripStartEndQuote(result);
					}
					break;							
				case 2 :
					if(cop.getType() != null){
						result = cop.getType().toString();
						if(cop.getType().isArrayType()){
							result += "[]";
						} else if(cop.getType().isReferenceType()){
							result += " ref";
						}
					} else {
						result = "null";
					}					
					break;
				default :
					break; 	
			}
			}
			return result;	
		}
		/**
		 * @param res
		 * @param i
		 */
		private String stripStartEndQuote(String res) {
			if(res.startsWith("\"")){
				res = res.substring(1);
			} 
			if(res.endsWith("\"")){
				res = res.substring(0, res.length() - 1);
			}
			return res;
		}
		public Image getColumnImage(Object obj, int index) {
			if(index == 0 && obj instanceof CIMProperty){
				CIMProperty cop = (CIMProperty) obj;
				String name = cop.getName();				
				if(keysList != null && keysList.contains(name)){
					return getImage(obj);	
				}
				
			}
			return null;
		}
		public Image getImage(Object obj) {
			
			return CIMPlugin.getDefault().getImage("icons/key.gif");
			
		}
	}
	class NameSorter extends ViewerSorter {
	}

	/**
	 * The constructor.
	 */
	public CIMInstanceView() {
	}

	/**
	 * This is a callback that will allow us
	 * to create the viewer and initialize it.
	 */
	public void createPartControl(Composite parent) {
		
		mainComposite = new Composite(parent, SWT.BORDER);
		GridLayout gl = new GridLayout();
		gl.numColumns = 1;
		gl.marginLeft = 0;
		gl.marginRight = 0;
		gl.marginTop = 0;
		gl.marginBottom = 0;
		gl.makeColumnsEqualWidth = true;
		gl.verticalSpacing = 0;
		gl.horizontalSpacing = 0;
		mainComposite.setLayout(gl);
		GridData gld = new GridData();
		gld.grabExcessHorizontalSpace = true;
		gld.grabExcessVerticalSpace = true;
		//gld.exclude = true;
		gld.verticalAlignment = SWT.FILL;
		gld.horizontalAlignment = SWT.FILL;
		mainComposite.setLayoutData(gld);
		
		copLabel = new Label(mainComposite, SWT.NONE | SWT.WRAP);		
		copLabel.setText("CIM Object Path");
		GridData lgd = new GridData();
		lgd.grabExcessHorizontalSpace = true;
		lgd.grabExcessVerticalSpace = false;
		lgd.horizontalAlignment = SWT.LEFT;
		copLabel.setLayoutData(lgd);
		
		createTable(mainComposite);
		addTableCellEditors();
		makeActions();
		hookContextMenu();
		//hookDoubleClickAction();
		contributeToActionBars();
		propChangeListener = new CIMPropChangeListener();
		CIMPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(propChangeListener);
	}
	
	private void addTableCellEditors() {
		final Table table = viewer.getTable();
		final TableEditor te = new TableEditor(table);
		te.grabHorizontal = true;
		te.horizontalAlignment = SWT.LEFT;
		table.addListener(SWT.MouseDown, new Listener() {
			public void handleEvent(Event event) {
			Rectangle clientArea = table.getClientArea();
	        Point pt = new Point(event.x, event.y);
	        int index = table.getTopIndex();
	        while (index < table.getItemCount()) {
	            boolean visible = false;
	            final TableItem item = table.getItem(index);
	            //for (int i = 0; i < table.getColumnCount(); i++) {
	            	int i = 1; //edit only value
	              Rectangle rect = item.getBounds(i);
	              if (rect.contains(pt)) {
	                final int column = i;
	                final Text text = new Text(table, SWT.NONE);
	                Listener textListener = new Listener() {
	                  public void handleEvent(final Event e) {
	                    switch (e.type) {
	                    case SWT.FocusOut:
	                    	setCIMValue(item, text);
	                      item.setText(column, text.getText());
	                      text.dispose();
	                      setInstanceAct.setEnabled(true);
	                      break;
	                    case SWT.Traverse:
	                      switch (e.detail) {
	                      case SWT.TRAVERSE_RETURN:
	                    	  
	                        item
	                            .setText(column, text
	                                .getText());
	                        setInstanceAct.setEnabled(true);
	                        setCIMValue(item, text);
	                      //FALL THROUGH
	                      case SWT.TRAVERSE_ESCAPE:
	                        text.dispose();
	                        e.doit = false;	                        
	                      }
	                      break;
	                    }
	                  }

					/**
					 * @param item
					 * @param text
					 */
					private void setCIMValue(final TableItem item, final Text text) {
						CIMProperty cp = (CIMProperty)item.getData();
						String newVal = text.getText();
						CIMDataType cdt = cp.getType();
						if(changedProps == null){
							changedProps = new ArrayList<String>();
						}
						changedProps.add(cp.getName());
						if(cdt.isArrayType()){
							String vals[] = newVal.split(",");
							if(cdt.getType() == CIMDataType.STRING_ARRAY){
								CIMValue newCimVal = new CIMValue(newVal, cdt);
								cp.setValue(newCimVal);
							} else {
								Vector v = new Vector(vals.length);
								for(int i = 0; i < vals.length; i++){
									if(cdt.getType() == CIMDataType.UINT8_ARRAY || 
											cdt.getType() == CIMDataType.UINT16_ARRAY ||
											cdt.getType() == CIMDataType.UINT32_ARRAY) {
										v.add(new Integer(vals[i]));
									} else if(cdt.getType() == CIMDataType.DATETIME_ARRAY){
										v.add(new CIMDateTime(vals[i]));
									}
								}
								CIMValue newCimVal = new CIMValue(v);
								cp.setValue(newCimVal);
							}
						} else {
							CIMValue newCimVal = null;
							if(cdt.getType() == CIMDataType.STRING){
								newCimVal = new CIMValue(newVal, cdt);
								
							} else if(cdt.getType() == CIMDataType.BOOLEAN){
								newCimVal = new CIMValue(new Boolean(newVal), cdt);
							} else if(cdt.getType() == CIMDataType.UINT8_ARRAY || 	                    		
									cdt.getType() == CIMDataType.UINT16_ARRAY ||
									cdt.getType() == CIMDataType.UINT32_ARRAY){
								newCimVal = new CIMValue(new Integer(newVal));
							} else if(cdt.getType() == CIMDataType.DATETIME){
								newCimVal = new CIMValue(new CIMDateTime(new Date(newVal)));
							}
							cp.setValue(newCimVal);
						}
					}
	                };
	                text.addListener(SWT.FocusOut, textListener);
	                text.addListener(SWT.Traverse, textListener);
	                te.setEditor(text, item, i);
	                text.setText(item.getText(i));
	                text.selectAll();
	                text.setFocus();
	                return;
	              }
	              if (!visible && rect.intersects(clientArea)) {
	                visible = true;
	              }
	            //}
	            if (!visible)
	              return;
	            index++;
	          }
			}
		});
	}
	

	/**
	 * @param parent
	 */
	private void createTable(Composite parent) {
		tableComp = new Composite(parent, SWT.BORDER |SWT.FILL);
		FillLayout fl = new FillLayout();		
		tableComp.setLayout(fl);
		GridData gld = new GridData();
		gld.grabExcessHorizontalSpace = true;
		gld.grabExcessVerticalSpace = true;
		gld.horizontalAlignment = SWT.FILL;
		gld.verticalAlignment = SWT.FILL;	
		tableComp.setLayoutData(gld);
		viewer = new TableViewer(tableComp, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
		viewer.setContentProvider(new ViewContentProvider());
		viewer.setLabelProvider(new ViewLabelProvider());
		viewer.setSorter(new NameSorter());
		viewer.setInput(getViewSite());		
		addTableColumns(viewer.getTable());
		viewer.getTable().setHeaderVisible(true);		
		viewer.setColumnProperties(COLUMN_NAMES);		
	}
	private void addTableColumns(Table objectPathTable){
		TableColumn column = new TableColumn(objectPathTable, SWT.LEFT, 0);		
		column.setText(COLUMN_NAMES[0]);
		column.setWidth(120);
		column = new TableColumn(objectPathTable, SWT.LEFT, 1);
		column.setText(COLUMN_NAMES[1]);
		column.setWidth(400);
		column = new TableColumn(objectPathTable, SWT.LEFT, 2);
		column.setText(COLUMN_NAMES[2]);
		column.setWidth(100);
		objectPathTable.setLinesVisible(true);
				
	}

	private void hookContextMenu() {
		MenuManager menuMgr = new MenuManager("#PopupMenu");
		menuMgr.setRemoveAllWhenShown(true);
		menuMgr.addMenuListener(new IMenuListener() {
			public void menuAboutToShow(IMenuManager manager) {
				CIMInstanceView.this.fillContextMenu(manager);
			}
		});
		Menu menu = menuMgr.createContextMenu(viewer.getControl());
		viewer.getControl().setMenu(menu);
		getSite().registerContextMenu(menuMgr, viewer);
	}

	private void contributeToActionBars() {
		IActionBars bars = getViewSite().getActionBars();
		fillLocalPullDown(bars.getMenuManager());
		fillLocalToolBar(bars.getToolBarManager());
	}

	private void fillLocalPullDown(IMenuManager manager) {
		manager.add(refreshAction);
		manager.add(setInstanceAct);
		//manager.add(new Separator());
		manager.add(deleteInstance);
		
	}

	private void fillContextMenu(IMenuManager manager) {
		manager.add(refreshAction);
		manager.add(setInstanceAct);
		manager.add(deleteInstance);
		// Other plug-ins can contribute there actions here
		manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
	}
	
	private void fillLocalToolBar(IToolBarManager manager) {
		manager.add(refreshAction);
		manager.add(setInstanceAct);
		manager.add(deleteInstance);
	}

	private void makeActions() {
		setInstanceAct = new Action() {
			public void run() {
				//get client
				CIMClient client = null;
				try {
					client = CIMConnect.connectUsingPreferences();					
					try {
						client.setInstance(cimInstance.getObjectPath(), cimInstance, 
								false, (String[])changedProps.toArray(new String[]{}));
						showMessage("Set instance successfull");
						setInstanceAct.setEnabled(false);
					} catch (CIMException e) {
						e.printStackTrace();
						ErrorDialog ed = new ErrorDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
								"Set Instance Error", "Set Instance failed:" + e.getMessage(), 
								new Status(Status.ERROR, "CIMPlugin", e.getXmlCode(e.getID()), e.getMessage(), e),
								IStatus.ERROR);
						ed.open();
					}			
				} finally {
					if(client != null){
					try {
						client.close();
					} catch (CIMException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					}
				}
				
			}
		};
		setInstanceAct.setText("Set instance");
		setInstanceAct.setToolTipText("Set instance");
		setInstanceAct.setImageDescriptor(CIMPlugin.getImageDescriptor("icons/save.gif"));
		setInstanceAct.setDisabledImageDescriptor(CIMPlugin.getImageDescriptor("icons/save_disabled.gif"));
		setInstanceAct.setEnabled(false);
		deleteInstance = new Action() {
			public void run() {				
				CIMClient client = CIMConnect.connectUsingPreferences();
				try {
					MessageDialog areYouSure = new MessageDialog(
							PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
							"Delete Instance?", MessageDialog.getImage(
									MessageDialog.DLG_IMG_QUESTION),
							"Delete this instance?", MessageDialog.QUESTION,
							new String[]{"OK", "Cancel"}, 0);
					areYouSure.open();
					if(areYouSure.getReturnCode() == MessageDialog.OK) {
					client.deleteInstance(cimInstance.getObjectPath());
					clearView();
					CIMExplorerView cev = (CIMExplorerView) 
					PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().showView(
							"com.ultimatetech.cim.views.CIMExplorerView");
					cev.refreshAllTreeNodes();
					}
				} catch (CIMException e) {
					ErrorDialog ed = new ErrorDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
							"Delete Instance Error", "Delete instance failed:" + e.getMessage(), 
							new Status(Status.ERROR, "CIMPlugin", e.getXmlCode(e.getID()), e.getMessage(), e),
							IStatus.ERROR);
					ed.open();
				} catch (Exception e){
					ErrorDialog ed = new ErrorDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
							"Delete Instance Error", "Delete instance failed:" + e.getMessage(), 
							new Status(Status.ERROR, "CIMPlugin", 100, e.getMessage(), e),
							IStatus.ERROR);
					ed.open();
				} finally {
					if(client != null){
						try {
							client.close();
						} catch (CIMException e) {							
							e.printStackTrace();
						}
					}
				}
				//showMessage("Delete instance not yet implemented");
			}
		};
		deleteInstance.setText("Delete instance");
		deleteInstance.setToolTipText("Delete instance");
		deleteInstance.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
				getImageDescriptor(ISharedImages.IMG_OBJS_ERROR_TSK));
		
		
		/*doubleClickAction = new Action() {
			public void run() {
				ISelection selection = viewer.getSelection();
				Object obj = ((IStructuredSelection)selection).getFirstElement();
				showMessage("Double-click detected on "+obj.toString());
			}
		};*/
		
		refreshAction = new Action() {
			public void run() {	
				CIMClient client = CIMConnect.connectUsingPreferences();
				try {
					cimInstance = client.getInstance(cimInstance.getObjectPath(), false, true, true, null);
					showMessage("Instance reloaded");
				} catch (CIMException e) {					
					e.printStackTrace();
					ErrorDialog ed = new ErrorDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
							"Reload Instance Error", "Reload Instance failed:" + e.getMessage(), 
							new Status(Status.ERROR, "CIMPlugin", e.getXmlCode(e.getID()), e.getMessage(), e),
							IStatus.ERROR);
					ed.open();
				}finally {
					if(client != null){
						try {
							client.close();
						} catch (CIMException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
				
				setInstanceAct.setEnabled(false);
			}
		};
		refreshAction.setText("Reload instance");
		refreshAction.setToolTipText("Reload instance");
		refreshAction.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
				getImageDescriptor(ISharedImages.IMG_TOOL_REDO));
	}

	/*private void hookDoubleClickAction() {
		viewer.addDoubleClickListener(new IDoubleClickListener() {
			public void doubleClick(DoubleClickEvent event) {
				doubleClickAction.run();
			}
		});
	}*/
	private void showMessage(String message) {
		MessageDialog.openInformation(
			viewer.getControl().getShell(),
			"CIM Instance View",
			message);
	}

	/**
	 * Passing the focus request to the viewer's control.
	 */
	public void setFocus() {
		viewer.getControl().setFocus();
	}
	
	public void setCIMInstance(CIMInstance ci){
		cimInstance = ci;
		if(viewer != null){
			keysList = new ArrayList<String>();
			Vector<CIMProperty> v = cimInstance.getKeys();
			int sz1 = v == null ? 0 : v.size();
			for(int i = 0; i < sz1; i++){
				CIMProperty cp = v.get(i);
				keysList.add(cp.getName());
			}
			viewer.getTable().removeAll();
			viewer.getTable().setHeaderVisible(true);
			viewer.refresh();			
			copLabel.setText(ci.getObjectPath().getObjectName());
			Point sz = tableComp.getSize();
			mainComposite.layout(true, true);
			tableComp.setSize(sz);
			
		}
	}
	public void dispose() {
		CIMPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(propChangeListener);
		super.dispose();
	}
}